const Service = require('egg').Service;
const fs = require('fs');
const path = require('path');
// https://stackoverflow.com/questions/31645738/how-to-create-full-path-with-nodes-fs-mkdirsync
const mkdirp = require('mkdirp'); // 用来创建文件目录
const FolderScanner = require('@moyufed/folder-scanner');

class CdnService extends Service {
    findCdns(data = {}) {
        return this.ctx.model.Cdn.find(data).sort('-createDate').lean();
    }

    findCdn(_id) {
        return this.ctx.model.Cdn.findOne({ _id }).lean();
    }

    createCdn(data) {
        if (!data.uploadType || data.uploadType === 1) {
            if (!data.version) {
                throw new Error('需要设置初始版本');
            }
            if (!data.authPage) {
                throw new Error('需要设置版本中间页');
            }
            if (!data.rule) {
                throw new Error('需要设置版本替换规则');
            }
        }
        return this.ctx.model.Cdn({ ...data }).save();
    }

    deleteCdn(id) {
        return this.ctx.model.Cdn.remove({ _id: id }).lean();
    }

    updateCdn(id, data) {
        return this.ctx.model.Cdn.findOneAndUpdate({ _id: id }, { ...data }, { new: true }).lean();
    }

    async invalidCdn(id) {
        return this.ctx.model.Cdn.findOneAndUpdate({ _id: id }, { status: 2 }, { new: true }).lean();
    }

    async validCdn(id) {
        return this.ctx.model.Cdn.findOneAndUpdate({ _id: id }, { status: 1 }, { new: true }).lean();
    }

    async publishCdn(id) {
        const ctx = this.ctx
        const cdn = await ctx.model.Cdn.findOne({
            _id: id
        });

        if (cdn) {
            const tenantId = cdn ? cdn.tenantId : 'unknown';
            if (!cdn.newVersion) {
                throw new Error("没有需要发布的包，请上传！");
            }
            const destDir = path.resolve(process.cwd(), 'upload', ctx.app.config.envType[cdn.env], ctx.app.config.tenantType[cdn.type], tenantId, cdn.newVersion.version);
            if (!fs.existsSync(path.join(destDir, cdn.newVersion.folderName))) {
                throw new Error("没有需要发布的包，请上传！");
            }
            if (!cdn.uploadType || cdn.uploadType === 1) {
                const oldVersion = await this.downloadHtml(cdn._id, cdn);
                if (Number(oldVersion.replace(/\./g, '')) >= Number(cdn.newVersion.version.replace(/\./g, ''))) {
                    throw new Error("线上已经有新版本的包，请重新上传！");
                }
            }
            await ctx.service.upload.uploadQiniu(destDir, cdn.newVersion.folderName, cdn.resource.replace('{version}', cdn.newVersion.version));
            if (!cdn.uploadType || cdn.uploadType === 1) {
                await this._replaceNewVersion(cdn);
                await ctx.service.upload.replaceAuthPage(cdn);
            }
            return this.updateCdn({ _id: cdn._id }, { version: cdn.newVersion.version })
        }
    }

    // async uploadFile(files, type, tenantId, version, cdn) {
    //     const ctx = this.ctx;
    //     const tenantMap = {
    //         "ctripvbk": "ctrip",
    //         "ctrip": "ctripadmin"
    //     }
    //     const id = tenantMap[tenantId] || tenantId
    //     const sourcePath = path.join('fileTemp', type, files[this.cdnIndex]);
    //     const destPath = cdn.resource.replace('{version}', version) + (files[this.cdnIndex]).replace(new RegExp('\/' + id + '\/', 'g'), '/');
    //     const res = await ctx.app.fullQiniu.uploadFile(destPath, sourcePath);
    //     this.cdnIndex++;
    //     console.log(this.cdnIndex + ':' + destPath);
    //     if (res.ok && files[this.cdnIndex]) {
    //         return await this.uploadFile(files, type, tenantId, version, cdn);
    //     }
    //     if (res.ok && this.cdnIndex === files.length) {
    //         const fileName = cdn.authPage.replace(/[\s\S]*\//g, '');
    //         const dest = cdn.authPage.replace(/https?:\/\/[\s\S]*.com\//g, '');
    //         console.log(dest);

    //         const res2 = await ctx.app.fullQiniu.uploadFile(dest, path.join('fileTemp', fileName));
    //         if (res2.ok) {
    //             return true
    //         }
    //         return false
    //     } else {
    //         return false
    //     }
    // }

    /**
     * 下载CDN站点的鉴权文件，并且更新站点版本
     *
     * @param {*} id CDN站点ID
     * @param {*} cdn CDN站点
     * @returns
     * @memberof CdnService
     */
    async downloadHtml(id, cdn) {
        const ctx = this.ctx;
        const { helper } = this.ctx;
        if (!cdn) {
            cdn = await this.ctx.model.Cdn.findOne({
                _id: id
            });
        }
        if (cdn && cdn.uploadType === 2) { // 直接上传模式
            return cdn.version || helper.getCurrentTime();
        }
        if (cdn) {
            const htmlPath = cdn.authPage + '?t=' + new Date().getTime();
            const result = await this.ctx.curl(htmlPath, {
                dataType: 'html',
            });
            if (result.status === 404) {
                throw new Error('OMG，中间页已经找不到，请排查问题！');
            }
            if (result.status !== 200) {
                throw new Error('OMG，获取中间页信息失败！');
            }
            const fileName = cdn.authPage.replace(/[\s\S]*\//g, '');
            const fileDir = path.resolve(process.cwd(), 'upload', 'auth', ctx.app.config.envType[cdn.env], ctx.app.config.tenantType[cdn.type], 'backup');
            if (!fs.existsSync(fileDir)) {
                // 也可以使用 fs-extra 库
                await mkdirp(fileDir);
            }
            const pathName = path.resolve(fileDir, fileName);
            await this.writeFile(pathName, result.data);
            return await this._getVersion(pathName, cdn)
        }
    }

    writeFile(output, data) {
        return new Promise((resolve, reject) => {
            fs.writeFile(output, data, err => {
                if (err) {
                    console.log(err);
                    reject(new Error('拉取CDN文件失败'));
                } else {
                    resolve(true);
                }
            })
        });
    }

    async _getVersion(filePath, cdn) {
        let content = fs.readFileSync(filePath, { encoding: "utf8" });
        const v = new RegExp(cdn.rule).exec(content);
        if (!v) {
            throw new Error('获取到的中间页与正则表达式不符，请检查！');
        }
        const version = v[0].replace(v[1], '');
        // const version = (/\/\d.\d.\d{1,}\//).exec(content)[0].replace(/\//g, '');
        await this.ctx.model.Cdn.findOneAndUpdate({
            _id: cdn._id
        }, {
            version
        });
        return version;
    }

    /**
     * 使用指定版本更新授权页中应用入口页版本
     *
     * @param {*} cdn
     * @returns
     * @memberof CdnService
     */
    async _replaceNewVersion(cdn) {
        const ctx = this.ctx;
        const fileName = cdn.authPage.replace(/[\s\S]*\//g, '');
        const fileDir = path.resolve(process.cwd(), 'upload', 'auth', ctx.app.config.envType[cdn.env], ctx.app.config.tenantType[cdn.type], 'backup');
        const fileDirNew = path.resolve(process.cwd(), 'upload', 'auth', ctx.app.config.envType[cdn.env], ctx.app.config.tenantType[cdn.type], 'new');
        const filePath = path.join(fileDir, fileName);
        const filePathNew = path.join(fileDirNew, fileName);
        if (!fs.existsSync(fileDirNew)) {
            await mkdirp(fileDirNew);
        }
        let newVer;
        const v = cdn.newVersion.version.split('.');
        const rule = new RegExp(cdn.rule);
        if (fs.existsSync(filePath)) {
            let content = fs.readFileSync(filePath, { encoding: "utf8" });
            content = content.replace(
                rule,
                function (match, withoutVerPart, majorVer, minorVer, revisionVer) {
                    newVer = [majorVer, minorVer, revisionVer]
                        .map((version, index) => v[index])
                        .join(".");
                    return withoutVerPart + newVer;
                }
            );
            // 针对通用钉钉版做处理
            if (cdn.type === 4 && cdn.tenantId === 'common') {
                content = content.replace(
                    /(\(tenantId\)[\S\s]*?https:\/\/res.exexm.com\/dingtalk\/)(\d).(\d).(\d{1,})/g,
                    function (match, withoutVerPart, majorVer, minorVer, revisionVer) {
                        newVer = [majorVer, minorVer, revisionVer]
                            .map((version, index) => v[index])
                            .join(".");
                        return withoutVerPart + newVer;
                    }
                );
            }
            await this.writeFile(filePathNew, content);
            return newVer;
        } else {
            throw new Error('本地鉴权页已经丢失，请重新上传包！');
        }
    }

    /**
     * 自动更新授权页中应用入口页版本
     *
     * 主版本号.子版本号[.修正版本号[.编译版本号]]
     * Major_Version_Number.Minor_Version_Number[.Revision_Number[.Build_Number]]
     *
     * @param {*} updateType 更新版本类型，主版本-0，子版本-1，修正版本-2
     */
    async _updateHtmlVersion(cdn, updateType = 2) {
        const ctx = this.ctx;
        const fileName = cdn.authPage.replace(/[\s\S]*\//g, '');
        const fileDir = path.resolve(process.cwd(), 'upload', 'auth', ctx.app.config.envType[cdn.env], ctx.app.config.tenantType[cdn.type], 'backup');
        const fileDirNew = path.resolve(process.cwd(), 'upload', 'auth', ctx.app.config.envType[cdn.env], ctx.app.config.tenantType[cdn.type], 'new');
        const filePath = path.join(fileDir, fileName);
        const filePathNew = path.join(fileDirNew, fileName);
        if (!fs.existsSync(fileDirNew)) {
            await mkdirp(fileDirNew);
        }
        const rule = new RegExp(cdn.rule);
        let newVer;
        if (fs.existsSync(filePath)) {
            let content = fs.readFileSync(filePath, { encoding: "utf8" });
            content = content.replace(
                rule,
                function (match, withoutVerPart, majorVer, minorVer, revisionVer) {
                    newVer = [majorVer, minorVer, revisionVer]
                        .map((version, index) => {
                            return index === updateType
                                ? Number(version) + 1
                                : Number(version);
                        })
                        .join(".");
                    return withoutVerPart + newVer;
                }
            );
            await this.writeFile(filePathNew, content);
            return newVer;
        } else {
            throw new Error('本地鉴权页已经丢失，请重新上传包！');
        }
    }
}

module.exports = CdnService;
